/*
 * $Id: CreateRFSRequestHandler.java,v 1.9 2011/01/14 15:52:31 troybirk Exp $
 * 
 * Copyright (C) 2010 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.createrfs;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.createrfs.RFSCreator;
import com.ge.healthcare.autosc.common.createrfs.RFSLogger;
import com.ge.healthcare.autosc.common.database.dao.IJapanMagnetRuleidLookupDAO;
import com.ge.healthcare.autosc.common.database.pojo.CfgJapanMagnetRuleidLookup;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.FTRLog;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.createrfs.request.CreateRFSRequest;
import com.ge.healthcare.autosc.createrfs.response.CreateRFSResponse;
import com.ge.healthcare.autosc.processor.exception.AssistCaseCreationFailureException;
import com.ge.healthcare.autosc.processor.exception.IgnoreJapanMagnetAlarmException;
import com.ge.healthcare.autosc.processor.exception.RFSCreationFailureException;
import com.ge.med.service.eds.caseAPI.vo.AssistCaseCreateRequest;
import com.ge.med.service.eds.caseAPI.vo.AssistCaseCreateResponse;
import com.ge.med.service.eds.commons.DataException;
import com.ge.med.service.eds.commons.ResourceException;
import com.ge.med.service.eds.rfs.vo.RFSCreateCAPS;
import com.ge.med.service.eds.rfs.vo.RFSResponseCAPS;

/**
 * Handler for CreateRFSRequest messages.
 * 
 */
public class CreateRFSRequestHandler {

    // **Codes to use with with the FTRLog
    private static final String CAPS = "CAPS";
    private static final String ASSIST = "ASSIST";    
    private static final String RFS = "RFS";
    
    // Constants to use when creating the request messages.
    private static final String JAPAN_COUNTRY_CODE = "JP";
    private static final String JAPAN_ENTITY_CODE = "020";
    private static final String DS_CONTACTNAME = "DSAlarm";
    private static final String AUTOSC = "AUTOSC";
    private static final String NO_DATA_ERROR_MESSAGE = "DATA NOTHING";

    // Return status to be set in the response message.
    private static final String FAILED = "FAILED";
    private static final String IGNORED = "IGNORED";
    private static final String PARTIAL = "PARTIAL";    
    private static final String OK = "OK";

    /** Data access object for looking up Rule IDs based on symptom. */
    private IJapanMagnetRuleidLookupDAO japanMagnetRuleidLookupDAO;

    /** Class responsible for the creation of CAPS RFS and Assist Case. */
    private RFSCreator rfsCreator = new RFSCreator();

    /**
     * Constructor.
     */
    public CreateRFSRequestHandler() {
        super();
    }

    /**
     * Setter for the RFS creator.
     * 
     * @param rfsc RFS creator
     */
    public void setRfsCreator(RFSCreator rfsc) {
        rfsCreator = rfsc;
    }

    /**
     * Handler for a CreateRFSRequest. Lookup the rule id for the symptom in the
     * request message. If the symptom is not found, ignore the request.
     * Otherwise create a RFSCreateCAPS and pass that to the IRFS interface. If
     * the RFS creation was successful, then attempt to create an assist case.
     * 
     * @param req the request message to be handled.
     * @return response message. The response message will contain a status that
     *         will indicate if the request was successful or not.
     */
    public CreateRFSResponse handle(Object req) {
        CreateRFSResponse resp = null;
        CreateRFSRequest request = (CreateRFSRequest) req;
        try {
            resp = handleCreateRFSRequest(request);
        } catch (RFSCreationFailureException e) {
            RFSLogger.fatal(this.getClass(), "handle", e);
            FTRLog.log(FTRLog.JRFS, RFS, CAPS, e);
            resp = new CreateRFSResponse(null, null, FAILED, e.getMessage());
        } catch (AssistCaseCreationFailureException e) {
            RFSLogger.fatal(this.getClass(), "handle", e);
            FTRLog.log(FTRLog.JRFS, RFS, ASSIST, e);
            resp = new CreateRFSResponse(e.getRfsNumber(), null, PARTIAL, e.getMessage());
        } catch (Exception e) {
            String msg = createFailureMessage(e, "Exception thrown handling request", request.getSystemid());
            RFSLogger.fatal(this.getClass(), "handle", msg, e);
            FTRLog.log(FTRLog.JRFS, RFS, CAPS, new RFSCreationFailureException(msg));
            resp = new CreateRFSResponse(null, null, FAILED, msg);
        }

        return resp;
    }

    /**
     * Handler for a CreateRFSRequest. Lookup the rule id for the symptom in the
     * request message. If the symptom is not found, ignore the request.
     * Otherwise create a RFSCreateCAPS and pass that to the RFSCreator. If
     * the RFS creation was successful, then attempt to create an assist case.
     * 
     * @param req the request message to be handled.
     * @return response message. The response message will contain a status that
     *         will indicate if the request was successful or not.
     * @throws ResourceException thrown by handleNewRFS method
     * @throws DataException thrown by handleNewRFS method
     * @throws RFSCreationFailureException thrown by handleNewRFS method
     * @throws AssistCaseCreationFailureException thrown by handleNewRFS method
     */

    private CreateRFSResponse handleCreateRFSRequest(CreateRFSRequest req) throws ResourceException, DataException,
            RFSCreationFailureException, AssistCaseCreationFailureException {

        if (ASCUtil.isNullOrEmpty(req.getSystemid())) {
            RFSLogger.fatal("System ID is required");
            return new CreateRFSResponse(null, null, FAILED, "System ID is required");
        }

        CfgJapanMagnetRuleidLookup rule = lookupRuleId(req.getSymptom2WithValueRemoved());

        if (rule == null) {
            rule = lookupRuleId(req.getSymptom2());
            if (rule == null) {
                String msg = "System ID " + req.getSystemid() + " had alert ignored for symptom: " + req.getSymptom2();
                RFSLogger.info(msg);
                FTRLog.log(FTRLog.JRFS, RFS, CAPS, new IgnoreJapanMagnetAlarmException(msg));
                return new CreateRFSResponse(null, null, IGNORED, msg);
            }
        }

        RFSCreateCAPS vo = getCAPSVO(req, rule);
        String existingRFS = null;
        try {
            existingRFS = rfsCreator.findRFSForSystem(vo.getSystemId(), rule.getRuleId(), getToDate(),
                    JAPAN_COUNTRY_CODE);
        } catch (DataException e) {
            if (e.getMessage().toUpperCase(Locale.getDefault()).startsWith(NO_DATA_ERROR_MESSAGE)) {
                // a DataException will be thrown if no RFS exists for a system ID, log an info message
                String msg = createFailureMessage(e, "Exception thrown looking up CAPS RFS", vo.getSystemId());
                RFSLogger.info(this.getClass(), "handle", msg);
            } else {
                throw e;
            }
        }

        if (ASCUtil.isNullOrEmpty(existingRFS)) {
            return handleNewRFS(vo, rule, req);
        } else {
            String message = "RFS already exists for systemID " + vo.getSystemId() + " and ruleID = " + rule.getRuleId();
            RFSLogger.info(message);
            return new CreateRFSResponse(null, null, IGNORED, message);
        }

    }

    /**
     * Handle an RFS creation request for a new RFS.
     * 
     * @param vo the request message to be sent to the RFSCreator.
     * @param rule contains values to use for a number of the CAPS/Assist Case fields
     * @param req request message received from the external client
     * @return response message. The response message will contain a status that
     *         will indicate if the request was successful or not.
     * @throws DataException thrown by createAssistCase method
     * @throws ResourceException thrown by createAssistCase method
     * @throws RFSCreationFailureException thrown if null returned by createCAPSRFS method
     * @throws AssistCaseCreationFailureException if error creating Assist Case
     */
    private CreateRFSResponse handleNewRFS(RFSCreateCAPS vo, CfgJapanMagnetRuleidLookup rule, CreateRFSRequest req)
            throws ResourceException, DataException, RFSCreationFailureException, AssistCaseCreationFailureException {

        RFSResponseCAPS responseVO = null;
        responseVO = rfsCreator.createCAPSRFS(vo);

        if (responseVO == null) {
            String failureMsg = "Null response received creating CAPS RFS for System Id " + vo.getSystemId();
            throw new RFSCreationFailureException(failureMsg);
        }

        return createAssistCase(responseVO.getRFSNumber(), vo, rule, req);
    }


    /**
     * Create an Assist Case for an RFS number, RFS request message, and Japan
     * magnet rule database row.
     * 
     * @param rfsNumber the RFS number to link to the Assist Case.
     * @param vo the request message that was used to create the CAPS RFS>
     * @param rule contains values to use for a number of the Assist Case fields
     * @param req request message received from the external client
     * @return response message. The response message will contain a status that
     *         will indicate if the request was successful or not.
     * @throws AssistCaseCreationFailureException thrown if a bad value is returned
     *             when creating Assist Case
     */
    private CreateRFSResponse createAssistCase(String rfsNumber, RFSCreateCAPS vo, CfgJapanMagnetRuleidLookup rule,
            CreateRFSRequest req) throws AssistCaseCreationFailureException {

        String code = null;
        
        AssistCaseCreateRequest acReq = createAssistCaseCreateRequest(rfsNumber, vo, rule, req);
        AssistCaseCreateResponse assistCaseCreateResponse=null;
        try {
            assistCaseCreateResponse = rfsCreator.createAssistCase(acReq);
        } catch (Exception e) {
            String msg = createFailureMessage(e, "Exception thrown creating Assist Case", vo.getSystemId());
            throw new AssistCaseCreationFailureException(msg, code, rfsNumber);            
        } 
        
        if (assistCaseCreateResponse == null) {
            String failureMsg = "Null response received creating Assist Case for system ID " + vo.getSystemId();
            throw new AssistCaseCreationFailureException(failureMsg, code, rfsNumber);
        } else {
            String error = assistCaseCreateResponse.getError();
            // if the error is null or empty, all is good
            if (ASCUtil.isNullOrEmpty(error)) {
                CreateRFSResponse resp = new CreateRFSResponse(rfsNumber, assistCaseCreateResponse.getAssistNo(), OK,
                        null);
                RFSLogger.info("Success: " + resp.toString());
                return resp;
            } else {
                String msg = "Error creating Assist Case for system ID " + vo.getSystemId() + ". Error =  " + error;
                throw new AssistCaseCreationFailureException(msg, code, rfsNumber);
            }
        }

    }

    /**
     * Create an assist case request object for an RFS, CAPs request object, and
     * rule ID database row.
     * 
     * @param existingRFS the RFS number to link to the Assist Case being created.
     * @param vo the request object that was used to create the CAPS RFS.
     * @param rule database row containing fields associated with an alert symptom.
     * @param req request message received from the external client
     * @return request object for creating an assist case.
     */
    public AssistCaseCreateRequest createAssistCaseCreateRequest(String existingRFS, RFSCreateCAPS vo,
            CfgJapanMagnetRuleidLookup rule, CreateRFSRequest req) {
        String estimatedTOF = createEstTimeOfFailure(rule.getCaseCreationTimeOffset().intValue());

        AssistCaseCreateRequest assistobj = new AssistCaseCreateRequest();
        assistobj.setSystemId(vo.getSystemId());
        assistobj.setDispatchNumber(existingRFS);
        assistobj.setCustomerName(DS_CONTACTNAME);

        assistobj.setSymptoms(vo.getRfsNote());
        assistobj.setRfsType(rule.getRfsType().toString());
        assistobj.setApplicationName(AUTOSC);

        assistobj.setEstimatedSeverityOfFailure(rule.getProactiveSeverity().toString());
        assistobj.setEstTimeOfFailure(estimatedTOF);
        if (rule.getRuleId().indexOf("SHEM") > -1) {
            assistobj.setTechLog(req.getSymptom2WithValueRemoved() + " = " + req.getValue());
        }
        
        if (rule.getRuleId().indexOf("MM3") > -1) {
            String dragonUrl = ApplicationInfo.getInstance().getProperty(PropertyConstants.ASD_WEB_INTERFACE_URL);

            if (!ASCUtil.isNullOrEmpty(dragonUrl)) {
                if (dragonUrl.indexOf("<SYSTEM_ID>") != -1) {
                    dragonUrl = dragonUrl.replace("<SYSTEM_ID>", req.getSystemid());
                }
                assistobj.setTechLog(dragonUrl);
            }
        }

        return assistobj;

    }
    
    

    /**
     * Convenience method to create an error message for an exception, message
     * text, and system ID.
     * 
     * @param e the exception that was thrown.
     * @param text message text that will be displayed in the log.
     * @param systemId the system that the exception was generated for.
     * @return message to be logged and returned in the response message.
     */
    private String createFailureMessage(Exception e, String text, String systemId) {
        StringBuilder sb = new StringBuilder();
        sb.append(text);
        sb.append(" for system ID ");
        sb.append(systemId);
        sb.append(". Error = ");
        sb.append(e.getMessage());
        return sb.toString();

    }

    /**
     * Lookup rule based on symptom.
     * 
     * @param symptom symptom associated with the alert.
     * 
     * @return Rule associated with the symptom.
     */
    private CfgJapanMagnetRuleidLookup lookupRuleId(String symptom) {

        List rules = japanMagnetRuleidLookupDAO.findBySymptom(symptom);
        if (rules.size() == 1) {
            CfgJapanMagnetRuleidLookup obj = (CfgJapanMagnetRuleidLookup) rules.get(0);
            return obj;
        }

        return null;
    }

    /**
     * Create an RFSCreateCAPS object for the CreateRFSRequest object and rule
     * ID.
     * 
     * @param req the create RFS request object received from the client.
     * @param rule database table row associated with the alert symptom.
     * @return create CAPS RFS request object.
     */
    private RFSCreateCAPS getCAPSVO(CreateRFSRequest req, CfgJapanMagnetRuleidLookup rule) {
        RFSCreateCAPS vo = new RFSCreateCAPS();

        vo.setSystemId(req.getShortSystemId());
        vo.setEquipmentStatusDate(req.getEquipmentStatusDate());
        vo.setLegalEntityCode(JAPAN_ENTITY_CODE); // 020 is for JPN.
        vo.setCountryCode(JAPAN_COUNTRY_CODE);
        vo.setContactName(rule.getCustomerName());
        vo.setSymptom(rule.getServiceClass());
        vo.setRfsNote(rule.getRuleId());
        vo.setBadgeIdToAssign(rule.getBadgeToAssign());

        return vo;
    }

    /**
     * Create an Estimated Time of failure string. Set data in "yyyyMMddHHmm"
     * format. Should be Japan Standard Time(GMT+9). Cannot be null or empty.
     * Dependent on EDS Fix. --- Estimation failure should be optional for
     * ContactGE front. --- EDS/ASSIST team should make this as optional field.
     * 
     * @param offset number of days to add to the estimated TOF.
     * @return string representation of the estimated time of failure.
     */
    private String createEstTimeOfFailure(int offset) {

        Calendar curentTime = Calendar.getInstance();
        curentTime.add(Calendar.DATE, offset);

        SimpleDateFormat estTimeOfFailureFormat = new SimpleDateFormat("yyyyMMddHHmm");
        estTimeOfFailureFormat.setTimeZone(TimeZone.getTimeZone("GMT+9"));
        curentTime.setTimeZone(TimeZone.getTimeZone("GMT"));

        return estTimeOfFailureFormat.format(curentTime.getTime());

    }

    /**
     * Create the to date that will be used when searching for existing RFSs.
     * Should be Japan Standard Time(GMT+9). Cannot be null or empty.
     * 
     * @return String representation of the to date for searches.
     */
    private String getToDate() {

        Calendar curentTime = Calendar.getInstance();
        SimpleDateFormat toDateFormat = new SimpleDateFormat("yyyyMMdd");
        toDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+9"));
        curentTime.setTimeZone(TimeZone.getTimeZone("GMT"));
        return toDateFormat.format(curentTime.getTime());

    }

    /**
     * Setter for the rule ID lookup Data Access Object.
     * 
     * @param dao data access object used for looking up rules from the database
     */
    public void setJapanMagnetRuleidLookupDAO(IJapanMagnetRuleidLookupDAO dao) {
        japanMagnetRuleidLookupDAO = dao;
    }

}
